<?php

/**
 * Pwiff - PHP SWF Library
 *
 * LICENSE
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 *
 * @package    Pwiff
 * @subpackage Records
 * @copyright  Copyright (c) 2010 Steven Harris
 * @license    http://www.opensource.org/licenses/bsd-license.php New BSD License
 * @version    $Id: Matrix.php 5 2010-02-14 05:08:39Z indigo.sunrise $
 */

/**
 * Represents a Matrix record.
 *
 * @package    Pwiff
 * @subpackage Records
 */
class Pwiff_Record_Matrix extends Pwiff_Record
{
	/**#@+
	 * @var float
	 */
	/**
	 * Scale X value.
	 */
	protected $scaleX = null;
	
	/**
	 * Scale Y value.
	 */
	protected $scaleY = null;
	
	/**
	 * First rotation value.
	 */
	protected $rotateSkew0 = null;
	
	/**
	 * Second rotation value.
	 */
	protected $rotateSkew1 = null;
	/**#@-*/
	
	/**
	 * X translation value, in twips.
	 *
	 * @var int
	 */
	protected $translateX = 0;
	
	/**
	 * Y translation value, in twips.
	 *
	 * @var int
	 */
	protected $translateY = 0;
	
	/**
	 * Sets the values of the matrix in the form of an array, formatted
	 * as the matrix would be. To leave a value as unset, set it to null.
	 *
	 * Format:
	 *   array(
	 *     array( ScaleX,      RotateSkew0 ),
	 *     array( RotateSkew1, ScaleY ),
	 *     array( TranslateX,  TranslateY )
	 *  )
	 *
	 * @param  array See above format.
	 * @return Pwiff_Record_Matrix
	 */
	public function setMatrixArray(array $matrix)
	{
		// verify format
		if (sizeof($matrix) !== 3 OR sizeof($matrix[0]) !== 2
			OR sizeof($matrix[1]) !== 2 OR sizeof($matrix[2]) !== 2) {
			throw new Pwiff_Exception('Matrix array is in an invalid format.');
		}
		
		$this->setScaleX($matrix[0][0])
		     ->setScaleY($matrix[1][1])
		     ->setRotateSkew0($matrix[0][1])
		     ->setRotateSkew1($matrix[1][0])
		     ->setTranslateX($matrix[2][0])
		     ->setTranslateY($matrix[2][1]);
		return $this;
	}
	
	/**
	 * Sets the scale X value.
	 *
	 * @param  float $scaleX
	 * @return Pwiff_Record_Matrix
	 */
	public function setScaleX($scaleX)
	{
		$this->scaleX = $scaleX;
		return $this;
	}
	
	/**
	 * Sets the scale Y value.
	 *
	 * @param  float $scaleY
	 * @return Pwiff_Record_Matrix
	 */
	public function setScaleY($scaleY)
	{
		$this->scaleY = $scaleY;
		return $this;
	}
	
	/**
	 * Sets the first rotate/skew value.
	 *
	 * @param  float $rotateSkew0
	 * @return Pwiff_Record_Matrix
	 */
	public function setRotateSkew0($rotateSkew0)
	{
		$this->rotateSkew0 = $rotateSkew0;
		return $this;
	}
	
	/**
	 * Sets the second rotate/skew value.
	 *
	 * @param  float $rotateSkew1
	 * @return Pwiff_Record_Matrix
	 */
	public function setRotateSkew1($rotateSkew1)
	{
		$this->rotateSkew1 = $rotateSkew1;
		return $this;
	}
	
	/**
	 * Sets the X translation value in twips.
	 *
	 * @param  int $translateX
	 * @return Pwiff_Record_Matrix
	 */
	public function setTranslateX($translateX)
	{
		$this->translateX = $translateX;
		return $this;
	}
	
	/**
	 * Sets the Y translation value in twips.
	 *
	 * @param  int $translateY
	 * @return Pwiff_Record_Matrix
	 */
	public function setTranslateY($translateY)
	{
		$this->translateY = $translateY;
		return $this;
	}
	
	/**
	 * Gets the matrix values in array format. Unset values will be set
	 * to null.
	 *
	 * @see Pwiff_Record_Matrix::setMatrixArray()
	 * @return array
	 */
	public function getMatrixArray()
	{
		return array(
			array($this->scaleX,      $this->rotateSkew0),
			array($this->rotateSkew1, $this->scaleY),
			array($this->translateX,  $this->translateY),
		);
	}
	
	/**
	 * Gets the scale X value, or null if not set.
	 *
	 * @return float
	 */
	public function getScaleX()
	{
		return $this->scaleX;
	}
	
	/**
	 * Gets the scale Y value, or null if not set.
	 *
	 * @return float
	 */
	public function getScaleY()
	{
		return $this->scaleY;
	}
	
	/**
	 * Gets the first rotate/skew value, or null if not set.
	 *
	 * @return float
	 */
	public function getRotateSkew0()
	{
		return $this->rotateSkew0;
	}
	
	/**
	 * Gets the second rotate/skew value, or null if not set.
	 *
	 * @return float
	 */
	public function getRotateSkew1()
	{
		return $this->rotateSkew1;
	}
	
	/**
	 * Gets the X translation value in twips.
	 *
	 * @return int
	 */
	public function getTranslateX()
	{
		return $this->translateX;
	}
	
	/**
	 * Gets the Y translation value in twips.
	 *
	 * @return int
	 */
	public function getTranslateY()
	{
		return $this->translateY;
	}
	
	/**
	 * Gets the length of this matrix record.
	 * 
	 * @return int
	 */
	public function getRecordLength()
	{
		$bits = 7; // has scale, has rotate, num translate bits always here
		
		$bits += 2 * Pwiff_Util_Numerics::getSignedBitCount(array(
			$this->translateX,
			$this->translateY,
		));
		
		if ($this->scaleX !== null OR $this->scaleY !== null) {
			$bits += 5; // nScaleBits
			$bits += 2 * Pwiff_Util_Numerics::getFixedPointBitCount(array(
				(float) $this->scaleX,
				(float) $this->scaleY,
			));
		}
		
		if ($this->rotateSkew0 !== null OR $this->rotateSkew1 !== null) {
			$bits += 5; // nRotateBits
			$bits += 2 * Pwiff_Util_Numerics::getFixedPointBitCount(array(
				(float) $this->rotateSkew0,
				(float) $this->rotateSkew1,
			));
		}
		
		return ceil($bits / 8);
	}
	
	/**
	 * Writes the matrix values to the given output instance.
	 *
	 * @param  Pwiff_Output $out
	 */
	public function write(Pwiff_Output $out)
	{
		$out->byteAlign();
		
		// write the scale information if we have it
		if ($this->scaleX !== null OR $this->scaleY !== null) {
			$out->writeBits(1, 1);
			$nBits = Pwiff_Util_Numerics::getFixedPointBitCount(array(
				(float) $this->scaleX,
				(float) $this->scaleY,
			));
			$out->writeBits($nBits, 5);
			$out->writeFixedBits((float) $this->scaleX, $nBits);
			$out->writeFixedBits((float) $this->scaleY, $nBits);
		}
		else {
			$out->writeBits(0, 1);
		}
		
		// write the rotate/skew information if we have it
		if ($this->rotateSkew0 !== null OR $this->rotateSkew1 !== null) {
			$out->writeBits(1, 1);
			$nBits = Pwiff_Util_Numerics::getFixedPointBitCount(array(
				(float) $this->rotateSkew0,
				(float) $this->rotateSkew1,
			));
			$out->writeBits($nBits, 5);
			$out->writeFixedBits((float) $this->rotateSkew0, $nBits);
			$out->writeFixedBits((float) $this->rotateSkew1, $nBits);
		}
		else {
			$out->writeBits(0, 1);
		}
		
		// write the translation information
		$nBits = Pwiff_Util_Numerics::getSignedBitCount(array($this->translateX, $this->translateY));
		$out->writeBits($nBits, 5);
		$out->writeBits($this->translateX, $nBits);
		$out->writeBits($this->translateY, $nBits);
	}
}
